/*****************************************************************************
 * kal_linux_thread.c
 *
 * Copyright (C) 2019 Jeasonvor <1101627719@qq.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 ****************************************************************************/

/*****************************************************************************
 * Included Files
 ****************************************************************************/

#include "plum_api.h"

/*****************************************************************************
 * Trace Definitions
 ****************************************************************************/
#define LOG_RUN_LEVEL LOG_LEVEL_ERROR
#define LOG_MODULE    "kal.thread"

/*****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

/*****************************************************************************
 * Private Types
 ****************************************************************************/

typedef struct {
    plum_void (*func)(plum_void *arg);
    plum_void *arg;
} kal_thread_cb_t;

typedef struct {
    plum_s8          name[PLUM_KAL_THREAD_NAME_MAX];
    pthread_t        hdl;
    kal_thread_cb_t  cb;
    struct list_head list;
} kal_thread_t;

/*****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

/*****************************************************************************
 * Private Data
 ****************************************************************************/

PLUM_PRIVATE kal_thread_t thread_head;

/*****************************************************************************
 * Public Data
 ****************************************************************************/

/*****************************************************************************
 * Private Functions
 ****************************************************************************/

PLUM_PRIVATE
kal_thread_t *kal_thread_find(PLUM_CONST plum_s8 *name)
{
    struct list_head *pos;
    list_for_each(pos, &thread_head.list)
    {
        kal_thread_t *node = list_entry(pos, kal_thread_t, list);
        if (strncmp((const char *)node->name, (const char *)name,
                    strlen((const char *)node->name)) == 0) {
            return (node);
        }
    }

    return (plum_null);
}

PLUM_PRIVATE
plum_s32 kal_thread_del(kal_thread_t *node)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        if (!node) {
            LOG_E("kal_thread_del param is null");
            rc = PLUM_ECODE_EPARA;
            break;
        }

        list_del(&node->list);
        plum_kal_mem_free(node);
    } while (0);

    return (rc);
}

PLUM_PRIVATE
kal_thread_t *kal_thread_register(PLUM_CONST plum_s8 *name,
                                  plum_void (*func)(plum_void *arg), void *arg)
{
    kal_thread_t *node = plum_null;
    do {
        if (!thread_head.list.next) {

            INIT_LIST_HEAD(&thread_head.list);
        }

        node = kal_thread_find(name);
        if (!node) {
            node = (kal_thread_t *)plum_kal_mem_malloc(sizeof(kal_thread_t));
            if (!node) {
                LOG_E("kal_timer_apply malloc err");
                break;
            }

            snprintf((char *)node->name, sizeof(node->name), "%s", name);
            node->cb.func = func;
            node->cb.arg  = arg;

            /** 保存线程信息 */
            list_add(&node->list, &thread_head.list);
        }
    } while (0);

    return (node);
}

PLUM_PRIVATE
plum_void *kal_thread_main(plum_void *arg)
{
    plum_s8 *name = (plum_s8 *)arg;

    kal_thread_t *node = kal_thread_find(name);
    if (!node) {
        LOG_E("not find the thread,named:%s", name);
        return (plum_null);
    }

    node->cb.func(node->cb.arg);

    kal_thread_del(node);
    return (plum_null);
}
/*****************************************************************************
 * Public Functions
 ****************************************************************************/

PLUM_PUBLIC
plum_s32 plum_kal_thread_create(PLUM_CONST plum_s8 *name,
                                plum_void (*task)(plum_void *arg),
                                plum_void *arg, plum_kal_thread_priority_t pri,
                                plum_u32 stack_sz)
{
    plum_s32 rc = PLUM_ECODE_OK;

    PLUM_UNUSED(pri);
    PLUM_UNUSED(stack_sz);

    do {
        if (!name) {
            LOG_E("thread name is null");
            rc = PLUM_ECODE_EPARA;
            break;
        }

        if (strlen((const char *)name) > PLUM_KAL_THREAD_NAME_MAX) {
            LOG_E("thread name is too long");
            rc = PLUM_ECODE_EMEM;
            break;
        }

        /** 申请资源存放线程信息 */
        kal_thread_t *node = kal_thread_register(name, task, arg);
        if (!node) {
            LOG_E("kal_thread_apply err");
            rc = PLUM_ECODE_EMEM;
            break;
        }

        /** 创建线程 */
        rc = pthread_create(&node->hdl, NULL, kal_thread_main, (void *)name);
        if (rc) {
            LOG_E("pthread_create err,rc:%d", rc);
            break;
        }

        /** 自动回收线程资源 */
        rc = pthread_detach(node->hdl);
        if (rc) {
            LOG_E("pthread_detach err,rc:%d", rc);
            break;
        }

        rc = plum_handle_register(node);
        if (rc < 0) {
            LOG_E("register thread fail!");
            break;
        }
        rc = PLUM_ECODE_OK;
        LOG_D("create a thread,named:%s", name);

    } while (0);

    return (rc);
}

PLUM_PUBLIC
plum_s32 plum_kal_thread_delete(PLUM_CONST plum_s8 *name)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        if (!name) {
            LOG_E("The thread name is null");
            rc = PLUM_ECODE_EPARA;
            break;
        }

        kal_thread_t *node = kal_thread_find(name);
        if (!node) {
            LOG_E("not find the thread,named:%s", name);
            rc = PLUM_ECODE_EMEM;
            break;
        }

        rc = pthread_cancel(node->hdl);
        if (rc) {
            LOG_E("pthread_cancel err,rc:%d", rc);
            break;
        }

        rc = kal_thread_del(node);
        if (rc) {
            LOG_E("kal_thread_del err,rc:%d", rc);
            break;
        }
        LOG_I("Have delelte '%s' thread", name);
    } while (0);

    return (rc);
}

PLUM_PUBLIC
plum_s32 plum_kal_thread_suspend(PLUM_CONST plum_s8 *name)
{
    plum_s32 rc = PLUM_ECODE_OK;

    LOG_I("Suspend '%s' thread", name);
    return (rc);
}

PLUM_PUBLIC
plum_s32 plum_kal_thread_resume(PLUM_CONST plum_s8 *name)
{
    plum_s32 rc = PLUM_ECODE_OK;

    LOG_I("Resume '%s' thread", name);
    return (rc);
}

PLUM_PUBLIC
plum_void plum_kal_thread_show(PLUM_CONST plum_s8 *name)
{
    if (name) {
        kal_thread_t *node = kal_thread_find(name);
        if (!node) {
            LOG_E("not find the thread,named:%s", name);
            return;
        }
        LOG_I("thread[%u] named:%s", node->hdl, node->name);
    }
    else {
        struct list_head *pos;
        list_for_each(pos, &thread_head.list)
        {
            kal_thread_t *node = list_entry(pos, kal_thread_t, list);
            LOG_I("thread[%u] named:%s", node->hdl, node->name);
        }
    }
}

PLUM_PUBLIC
plum_void plum_kal_thread_delay(plum_u32 ms)
{
    usleep(ms * 1000);
}

/****************************************************************************/
/*                                                                          */
/*  End of file.                                                            */
/*                                                                          */
/****************************************************************************/
